package algorithm;

import graph.Graph;
import graph.GraphBackup;
import graph.Node;
import graph.Node_Scheduler;
import graph.Node_System;

import java.util.ArrayList;

@SuppressWarnings("all")
public class Attractors {
	private Graph graph;

	private ArrayList<Node> bad_nodes_system;
	private ArrayList<Node> bad_nodes_scheduler;
	
	private ArrayList<Node> visited_system;
	private ArrayList<Node> visited_scheduler;
	
	public Attractors(Graph g){
		graph = g;
	}
	int step = 0;

	public ArrayList<ArrayList<Node>> run(){
		if (graph.failure_nodes_system().size() == 0 || graph.failure_nodes_scheduler().size() == 0)
			graph.calculate_bad_nodes();
		bad_nodes_system = (ArrayList<Node>) graph.failure_nodes_system().clone();
		bad_nodes_scheduler = (ArrayList<Node>) graph.failure_nodes_scheduler().clone();
		
		visited_system = new ArrayList<Node>();
		visited_scheduler = new ArrayList<Node>();
		
		ArrayList<Node_System> oldFailureNodesSystem;
		ArrayList<Node_Scheduler> oldFailureNodesScheduler;
		
		do{
			oldFailureNodesSystem = (ArrayList<Node_System>) bad_nodes_system.clone();
			oldFailureNodesScheduler = (ArrayList<Node_Scheduler>) bad_nodes_scheduler.clone();

			//for all nodes in failure state system
			for(Node fs_sys : bad_nodes_system){
				if(!is_visited_system(fs_sys)){
					//for all predecessor of a failure state system
					for(Node pre_sch : fs_sys.predecessors()){
						//if all successors are failure states, if there is no possible choice
						boolean failure = true;
						for(Node succ_sys : pre_sch.successors()){
							if(!is_in_bad_nodes_system(succ_sys)){
								failure = false;
								break;
							}
						}
						if (failure){
							if(!is_in_bad_nodes_scheduler(pre_sch)){
								//add to set of failure states scheduler
								bad_nodes_scheduler.add((Node_Scheduler) pre_sch);
							}
						}
	
					}
				}
				visited_system.add(fs_sys);
			}


			//for all nodes in failure state scheduler
			for(Node fs_sch : bad_nodes_scheduler){
				if(!is_visited_scheduler(fs_sch)){
					//for all predecessors of a failure state
					for(Node pre_sys : fs_sch.predecessors()){
						//predecessor already known failure state ?
						if(!is_in_bad_nodes_system(pre_sys)){
							bad_nodes_system.add((Node_System) pre_sys);
						}
					}
					
				}
				visited_scheduler.add(fs_sch);
			}
			
//		print results
			System.out.println(step);
			step++;
			System.out.println(bad_nodes_system.size() + " " + oldFailureNodesSystem.size() + " " + bad_nodes_scheduler.size() + " " +  oldFailureNodesScheduler.size());

		}while((bad_nodes_system.size() != oldFailureNodesSystem.size()) || (bad_nodes_scheduler.size() != oldFailureNodesScheduler.size()));
		ArrayList<ArrayList<Node>> result = new ArrayList<ArrayList<Node>>();
		result.add(bad_nodes_system);
		result.add(bad_nodes_scheduler);
		return result;
	}
	

	private boolean is_in_bad_nodes_system(Node n){
		for(Node n_sys : bad_nodes_system){
			if (n_sys.equals(n)){
				return true;
			}
		}
		
		return false;
	}
	
	private boolean is_in_bad_nodes_scheduler(Node n){
		for(Node n_sch : bad_nodes_scheduler){
			if (n_sch.equals(n)){
				return true;
			}
		}
		return false;
	}
	private boolean is_visited_system(Node n){
		for (Node n_sys : visited_system){
			if (n_sys.equals(n))
				return true;
		}
		return false;
	}
	
	private boolean is_visited_scheduler(Node n){
		for (Node n_sch : visited_scheduler){
			if (n_sch.equals(n))
				return true;
		}
		return false;
	}
	
	public ArrayList<Node> bad_nodes_system(){
		return bad_nodes_system;
	}
	
	public ArrayList<Node> bad_nodes_scheduler(){
		return bad_nodes_scheduler;
	}
}
